package jtg.junit;

import freemarker.template.Configuration;
import freemarker.template.DefaultObjectWrapperBuilder;
import freemarker.template.Template;

import java.io.File;
import java.io.FileWriter;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.*;
import java.util.stream.Collectors;

public class JunitGeneratorNoAssert {

    //类路径
    private String clsPath;
    //类名 类的全限定名
    private String clsName;
    //方法名
    private String mtdName;
    //待测类
    private Class<?> aClass;
    //待测方法
    private Method aMethod;

    //模板参数 使用FreeMaker需要的模板
    private Map<Object, Object> form;

    //配置
    private Configuration cfg;

    private static List<String> BASIC_TYPE = Arrays.asList(
            "byte", "short", "int", "long", "float", "double", "boolean", "char"
    );

    public JunitGeneratorNoAssert(String clsName, String mtdName) throws Exception {
        this.clsName = clsName;
        this.mtdName = mtdName;
        this.form = new HashMap<>();
        System.out.println(clsName);
        System.out.println(mtdName);
        cfg = new Configuration(Configuration.VERSION_2_3_30); // 创建配置对象，并指定 FreeMarker 版本
        // 设置模板文件加载路径
        cfg.setDirectoryForTemplateLoading(new File("./src/main/resources/templates"));
        // 设置默认编码
        cfg.setDefaultEncoding("UTF-8");
        // 设置对象包装器
        cfg.setObjectWrapper(new DefaultObjectWrapperBuilder(Configuration.VERSION_2_3_30).build());
        reflectGet();
    }

    private void reflectGet() throws Exception {
        try {
            aClass = Class.forName(clsName); // 类的全限定名
            for (Method m : aClass.getMethods()) {
                if (m.getName().equals(mtdName)) {
                    aMethod = m;
                }
            }
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }

        // 获取待测方法的返回值类型，
        // 如果是BASIC_TYPE那么返回1 String返回0 数组返回3 其他情况返回2
        form.put("extraPackage", new ArrayList<String>());
        if (BASIC_TYPE.contains(aMethod.getReturnType().getSimpleName())) {
            form.put("returnType", 1);
        } else if (aMethod.getReturnType().getSimpleName().equals("String")) {
            form.put("returnType", 0);
        } else if (aMethod.getReturnType().getSimpleName().contains("[]")) {
            form.put("returnType", 2);
        } else {
            form.put("returnType", 2);
            List<String> extraPackage = (List<String>) form.get("extraPackage");
            extraPackage.add(aMethod.getReturnType().getName());
        }

        // 获取返回值类型的简单名称
        form.put("returnTypeName", aMethod.getReturnType().getSimpleName());
        // 获取所在包, 完整类名，简单类名，方法名
        form.put("completeClassName", aClass.getName());
        form.put("package", aClass.getPackage().getName());
        form.put("simpleClassName", aClass.getSimpleName());
        form.put("methodName", aMethod.getName());

        //初始化参数列表
        form.put("parameters", new ArrayList<List<Object>>());
        //初始化期待返回值列表
        //form.put("expects", new ArrayList<>());
        //初始化参数类型列表
        List<Integer> paraType = Arrays.stream(aMethod.getParameterTypes())
                //从目标方法 (aMethod) 获取参数类型数组，并将其转换为流（Stream）
                .map(type -> {
                    //对流中的每个参数类型进行映射处理。在这里，使用了一个 Lambda 表达式，针对每个参数类型执行不同的逻辑。
                    if (BASIC_TYPE.contains(type.getSimpleName()))
                        return 1;
                        // 表示Java基本类型
                    else if (type.getSimpleName().equals("String"))
                        return 0;
                        //如果参数类型是 String，将 1 添加到映射后的列表中，表示这是一个 String 类型参数。
                    else if (type.getSimpleName().contains("[]"))
                        return 3;
                        // 如果是一个数组
                    else {
                        List<String> extraPackage = (List<String>) form.get("extraPackage");
                        if (!extraPackage.contains(type.getName())) extraPackage.add(type.getName());
                        return 2;
                        //如果参数类型不是前面所述的情况，会将参数类型添加到 extraPackage 列表中，
                        // 并将 2 添加到映射后的列表中，表示这是一个 Object 类型参数。
                    }
                })
                //将映射后的结果收集到一个列表中，形成了表示参数类型的整数列表。
                .collect(Collectors.toList());
        //将参数类型列表存储在 root 中的 "parameterType" 键下。
        form.put("parameterType", paraType);

        //获取目标方法 (aMethod) 的参数类型名称列表，并将其存储到 root Map 中
        // 获取目标方法参数类型的简单名称列表
        List<String> paraTypeNames = Arrays.stream(aMethod.getParameterTypes())
                .map(Class::getSimpleName) // 获取参数类型的简单名称
                .collect(Collectors.toList()); // 收集为列表
        // 将参数类型名称列表存储到 root Map 中
        //form.put("parameterTypeName", paraTypeNames);

        // 是否为静态方法
        form.put("isStatic", Modifier.isStatic(aMethod.getModifiers()));
    }

    public void addPara(List<Object> parameter) {
        List<List<Object>> parameterList = (List<List<Object>>) (form.get("parameters"));
        parameterList.add(parameter);
    }

    public void addExpect(Object expect) {
        List<Object> expects = (List<Object>) form.get("expects");
        expects.add(expect);
    }

    //接收一个字符串作为参数，然后将字符串中的单词的首字母大写。
    public static String title(String word) {
        if (word == null) return "";
        if (word.length() == 1) return word.toUpperCase();
        return word.substring(0, 1).toUpperCase() + word.substring(1);
    }

    public void makeTestCase() {
        try {
            //从配置中获取名为 "junitor.ftl" 的 FreeMarker 模板。 应该是form
            Template template = cfg.getTemplate("junitor2.ftl");
            //构建测试类的文件名。使用了目标类 (aClass) 的简单名称和目标方法 (aMethod) 名称的title版本来构造文件名
            String fileName = aClass.getSimpleName() + title(aMethod.getName());
            //创建一个 FileWriter 对象，将要生成的测试代码写入到名为 fileName + "Test.java" 的 Java 文件中。
            FileWriter out = new FileWriter(new File("./src/test/java/jtg/junit",fileName + "Test_1" +
                    ".java"));
            //利用 FreeMarker 模板引擎，将模板 junitor.ftl 中的内容使用 root Map 中的数据填充，
            // 生成测试代码，并将其输出到之前创建的文件中。
            template.process(form, out);
        } catch (Exception e) {
            e.printStackTrace();
        }
        //输出提示信息，指示生成的 JUnit 测试代码已完成，可以在根目录下找到生成的 Java 文件。
        System.out.println("junit测试代码自动生成完毕");
        System.out.println("请在test/java/jtg/junit目录下寻找Java文件");
    }

    // 只支持一个参数
    public static void JunitGenerate1Para(String clsName, String mtdName, Object input) throws Exception {
        JunitGeneratorNoAssert junitGenerator = new JunitGeneratorNoAssert(clsName,mtdName);
        List<Object> para = new ArrayList<>();
        para.add(input);
        junitGenerator.addPara(para);
//        Object expect1 = expect;
//        junitGenerator.addExpect(expect1);
        junitGenerator.makeTestCase();
    }

    /**
     * 一次性生成多个测试用例的代码
      */
    public static void JunitGenerateMul(String clsName, String mtdName, List<Object> input) throws Exception {
        JunitGeneratorNoAssert junitGenerator = new JunitGeneratorNoAssert(clsName,mtdName);
        int paraNum = input.size();
        for (int i = 0; i < paraNum; i++) {
            ArrayList<Object> param = new ArrayList<>();
            param.add(input.get(i));
            junitGenerator.addPara(param);
        }
//        Object expect1 = expect;
//        junitGenerator.addExpect(expect1);
        junitGenerator.makeTestCase();
    }
}
